#include "StdAfx.h"
#include "hook_op.h"

#define MAX_KEY 100
#define CTRLBIT 0x04
#define ALTBIT  0x02
#define SHIFTBIT 0x01 


#pragma data_seg("shareddata")
	HHOOK h_hook						= NULL;
	UINT hook_count						= 0;
	static UCHAR hot_key[MAX_KEY]		= {0};		/// hotkey
	static UCHAR hot_key_mask[MAX_KEY]	= {0};		/// flag for hotkey, value is VK_CONTRL or VK_NEMU or VK_SHIFT
	static HWND call_wnd[MAX_KEY]		= {0};		/// window associated with hotkey
	static int key_count				= 0;
	static UCHAR mask_bits				= 0;		/// 00000 Ctrl Alt Shift
#pragma data_seg()


hook_op::hook_op(void)
{
	this->is_init_ = FALSE;
	init();
}

hook_op::~hook_op(void)
{
	uninit();
}

bool hook_op::init()
{
	this->instance_ = AfxGetInstanceHandle();
	this->is_init_ = init_hot_key();
	return true;
}

void hook_op::uninit()
{
	if( this->is_init_)
	{
		verify_window();
		uninit_i();
		this->is_init_ = FALSE;
	}
}

void hook_op::set_nitofy_wnd(HWND h)
{
}

LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{	
	BOOL is_processed = FALSE;
	if( HC_ACTION == nCode )
	{
		if( (lParam & 0xc0000000) == 0xc0000000 )
		{
			/// Key up
			switch(wParam)
			{
			case VK_MENU:
				mask_bits &= ~ALTBIT;
				break;
			case VK_CONTROL:
				mask_bits &= ~CTRLBIT;
				break;
			case VK_SHIFT:
				mask_bits &= ~SHIFTBIT;
				break;
			default:	/// judge the key and send message
				break;
			}
			for( int index = 0; index < MAX_KEY; ++index )
			{
				if( call_wnd[index] == NULL )
					continue;
				if( IsWindow(call_wnd[index]) && (hot_key[index] == wParam)
					&& (hot_key_mask[index] == mask_bits) )
				{
					SendMessage(call_wnd[index], WM_HOTKEY, wParam, WM_KEYUP);
					is_processed = TRUE;
				}
			}
		}
		else if( (lParam&0xc000ffff) == 1 )
		{	
			/// Key down
			switch( wParam )
			{
			case VK_MENU:
				mask_bits |= ALTBIT;
				break;
			case VK_CONTROL:
				mask_bits |= CTRLBIT;
				break;
			case VK_SHIFT:
				mask_bits |= SHIFTBIT;
				break;
			default:	/// judge the key and send message
				break;
			}
			for( int index = 0; index < MAX_KEY; ++index )
			{
				if( call_wnd[index] == NULL )
					continue;

				if( IsWindow(call_wnd[index]) && (hot_key[index] == wParam)
					&&(hot_key_mask[index] == mask_bits) )
				{
					SendMessage(call_wnd[index], WM_HOTKEY, wParam, WM_KEYDOWN);
					is_processed = TRUE;
				}
			}
		}
		if( !is_processed )
		{
			for( int index = 0; index < MAX_KEY; ++index )
			{		
				if( ::IsWindowVisible(call_wnd[index]) )
				{
					{
						if( (lParam & 0xc0000000) == 0xc0000000 )
							SendMessage(call_wnd[index], WM_KEYUP, wParam, lParam);

						if( (lParam&0xc000ffff) == 1 )
							SendMessage(call_wnd[index], WM_KEYDOWN, wParam, lParam);
					}
				}
			}
		}
	}
	return CallNextHookEx(h_hook, nCode, wParam, lParam);	
}

BOOL hook_op::init_hot_key()
{
	if( h_hook != NULL )
	{
		hook_count++;
		return TRUE;
	}
	else
		h_hook = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)KeyboardProc, this->instance_, 0);

	if( h_hook != NULL )
		hook_count++;

	return (h_hook != NULL);
}
BOOL hook_op::uninit_i()
{   
	if( hook_count > 1 )
	{
		hook_count--;
		return TRUE;
	}
	BOOL unhooked = UnhookWindowsHookEx(h_hook);
	if( unhooked == TRUE )
	{
		hook_count = 0;
		h_hook = NULL;
	}
	return unhooked;
} 

BOOL hook_op::add_hot_key(HWND wnd,UCHAR key,UCHAR mask)
{
	BOOL is_added = FALSE;
	for( int index = 0; index < MAX_KEY; ++index )
	{
		if( call_wnd[index] == 0 )
		{
			call_wnd[index] = wnd;
			hot_key[index] = key;
			hot_key_mask[index] = mask;
			is_added = TRUE;
			key_count++;
			break;
		}
	}
	return is_added;
}

BOOL hook_op::delete_hot_key(HWND wnd, UCHAR key, UCHAR mask)
{
	BOOL is_removed = FALSE;
	for( int index = 0; index < MAX_KEY; ++index )
	{
		if( call_wnd[index] == wnd )
		{
			if( hot_key[index] == key && hot_key_mask[index] == mask )
			{
				call_wnd[index] = NULL;
				hot_key[index] = 0;
				hot_key_mask[index] = 0;
				is_removed = TRUE;
				key_count--;
				break;
			}
		}
	}
	return is_removed;
}

void hook_op::verify_window()
{
	for( int i = 0; i < MAX_KEY; ++i )
	{
		if( call_wnd[i] != NULL ) 
		{
			if( !IsWindow(call_wnd[i]) )
			{
				call_wnd[i] = NULL;
				hot_key[i] = 0;
				hot_key_mask[i] = 0;
				key_count--;
			}
		}
	}
}
